package com.gitee.wsl.security.password

import com.gitee.wsl.ext.string.isCharEquals
import com.gitee.wsl.ext.string.isNumeric


/**
 * 检测密码强度<br></br>
 * 来自：https://github.com/venshine/CheckPasswordStrength
 *
 * @author venshine
 * @since 5.7.3
 */
object PasswdStrength {
    /**
     * 简单密码字典
     */
    private val DICTIONARY = arrayOf<String?>(
        "password", "abc123", "iloveyou", "adobe123", "123123", "sunshine",
        "1314520", "a1b2c3", "123qwe", "aaa111", "qweasd", "admin", "passwd"
    )

    /**
     * 数字长度
     */
    private val SIZE_TABLE = intArrayOf(
        9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
        Int.Companion.MAX_VALUE
    )

    /**
     * 检查密码的健壮性
     *
     * @param passwd 密码
     * @return strength level
     */
    fun check(passwd: String): Int {
        requireNotNull(passwd) { "password is empty" }
        val len = passwd.length
        var level = 0

        // increase points
        if (countLetter(passwd, CHAR_TYPE.NUM) > 0) {
            level++
        }
        if (countLetter(passwd, CHAR_TYPE.SMALL_LETTER) > 0) {
            level++
        }
        if (len > 4 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) > 0) {
            level++
        }
        if (len > 6 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) > 0) {
            level++
        }

        if (len > 4 && countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) > 0 || countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) > 0 || countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) > 0 || countLetter(passwd, CHAR_TYPE.SMALL_LETTER) > 0 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) > 0 || countLetter(passwd, CHAR_TYPE.SMALL_LETTER) > 0 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) > 0 || countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) > 0 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) > 0
        ) {
            level++
        }

        if (len > 6 && countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) > 0 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) > 0 || countLetter(
                passwd,
                CHAR_TYPE.NUM
            ) > 0 && countLetter(passwd, CHAR_TYPE.SMALL_LETTER) > 0 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) > 0 || countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) > 0 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) > 0 || countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) > 0 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) > 0 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) > 0
        ) {
            level++
        }

        if (len > 8 && countLetter(passwd, CHAR_TYPE.NUM) > 0 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) > 0 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) > 0 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) > 0
        ) {
            level++
        }

        if (len > 6 && countLetter(passwd, CHAR_TYPE.NUM) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) >= 3 || countLetter(passwd, CHAR_TYPE.NUM) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) >= 3 || countLetter(passwd, CHAR_TYPE.NUM) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2 || countLetter(passwd, CHAR_TYPE.SMALL_LETTER) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) >= 3 || countLetter(passwd, CHAR_TYPE.SMALL_LETTER) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2 || countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) >= 3 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2
        ) {
            level++
        }

        if (len > 8 && countLetter(passwd, CHAR_TYPE.NUM) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) >= 2 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) >= 2 || countLetter(
                passwd,
                CHAR_TYPE.NUM
            ) >= 2 && countLetter(passwd, CHAR_TYPE.SMALL_LETTER) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2 || countLetter(passwd, CHAR_TYPE.NUM) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.CAPITAL_LETTER
            ) >= 2 && countLetter(passwd, CHAR_TYPE.OTHER_CHAR) >= 2 || countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) >= 2 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2
        ) {
            level++
        }

        if (len > 10 && countLetter(passwd, CHAR_TYPE.NUM) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) >= 2 && countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) >= 2 && countLetter(
                passwd,
                CHAR_TYPE.OTHER_CHAR
            ) >= 2
        ) {
            level++
        }

        if (countLetter(passwd, CHAR_TYPE.OTHER_CHAR) >= 3) {
            level++
        }
        if (countLetter(passwd, CHAR_TYPE.OTHER_CHAR) >= 6) {
            level++
        }

        if (len > 12) {
            level++
            if (len >= 16) {
                level++
            }
        }

        // decrease points
        if ("abcdefghijklmnopqrstuvwxyz".indexOf(passwd) > 0 || "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(passwd) > 0) {
            level--
        }
        if ("qwertyuiop".indexOf(passwd) > 0 || "asdfghjkl".indexOf(passwd) > 0 || "zxcvbnm".indexOf(passwd) > 0) {
            level--
        }
        if (passwd.isNumeric() && ("01234567890".indexOf(passwd) > 0 || "09876543210".indexOf(passwd) > 0)) {
            level--
        }

        if (countLetter(passwd, CHAR_TYPE.NUM) == len || countLetter(
                passwd,
                CHAR_TYPE.SMALL_LETTER
            ) == len || countLetter(passwd, CHAR_TYPE.CAPITAL_LETTER) == len
        ) {
            level--
        }

        if (len % 2 == 0) { // aaabbb
            val part1 = passwd.substring(0, len / 2)
            val part2 = passwd.substring(len / 2)
            if (part1 == part2) {
                level--
            }
            if (part1.isCharEquals() && part2.isCharEquals()) {
                level--
            }
        }
        if (len % 3 == 0) { // ababab
            val part1 = passwd.substring(0, len / 3)
            val part2 = passwd.substring(len / 3, len / 3 * 2)
            val part3 = passwd.substring(len / 3 * 2)
            if (part1 == part2 && part2 == part3) {
                level--
            }
        }

        if (passwd.isNumeric() && len >= 6 && len <= 8) { // 19881010 or 881010
            var year = 0
            if (len == 8 || len == 6) {
                year = passwd.substring(0, len - 4).toInt()
            }
            val size = sizeOfInt(year)
            val month = passwd.substring(size, size + 2).toInt()
            val day = passwd.substring(size + 2, len).toInt()
            if (year >= 1950 && year < 2050 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
                level--
            }
        }

        for (s in DICTIONARY) {
            if (passwd == s || s!!.contains(passwd)) {
                level--
                break
            }
        }

        if (len <= 6) {
            level--
            if (len <= 4) {
                level--
                if (len <= 3) {
                    level = 0
                }
            }
        }

        if (passwd.isCharEquals()) {
            level = 0
        }

        if (level < 0) {
            level = 0
        }

        return level
    }

    /**
     * Get password strength level, includes easy, midium, strong, very strong, extremely strong
     *
     * @param passwd 密码
     * @return 密码等级枚举
     */
    fun getLevel(passwd: String): PASSWD_LEVEL {
        val level = check(passwd)
        when (level) {
            0, 1, 2, 3 -> return PASSWD_LEVEL.EASY
            4, 5, 6 -> return PASSWD_LEVEL.MIDIUM
            7, 8, 9 -> return PASSWD_LEVEL.STRONG
            10, 11, 12 -> return PASSWD_LEVEL.VERY_STRONG
            else -> return PASSWD_LEVEL.EXTREMELY_STRONG
        }
    }

    /**
     * Check character's type, includes num, capital letter, small letter and other character.
     * 检查字符类型
     *
     * @param c 字符
     * @return 类型
     */
    private fun checkCharacterType(c: Char): CHAR_TYPE {
        if (c.code >= 48 && c.code <= 57) {
            return CHAR_TYPE.NUM
        }
        if (c.code >= 65 && c.code <= 90) {
            return CHAR_TYPE.CAPITAL_LETTER
        }
        if (c.code >= 97 && c.code <= 122) {
            return CHAR_TYPE.SMALL_LETTER
        }
        return CHAR_TYPE.OTHER_CHAR
    }

    /**
     * 计算密码中指定字符类型的数量
     *
     * @param passwd 密码
     * @param type   类型
     * @return 数量
     */
    private fun countLetter(passwd: String?, type: CHAR_TYPE?): Int {
        var count = 0
        if (null != passwd) {
            val length = passwd.length
            if (length > 0) {
                for (i in 0..<length) {
                    if (checkCharacterType(passwd.get(i)) == type) {
                        count++
                    }
                }
            }
        }
        return count
    }

    /**
     * calculate the size of an integer number
     *
     * @param x 值
     * @return 数字长度
     */
    private fun sizeOfInt(x: Int): Int {
        var i = 0
        while (true) {
            if (x <= SIZE_TABLE[i]) {
                return i + 1
            }
            i++
        }
    }

    /**
     * 密码等级枚举
     */
    enum class PASSWD_LEVEL {
        EASY, MIDIUM, STRONG, VERY_STRONG, EXTREMELY_STRONG
    }

    /**
     * 字符类型枚举
     */
    enum class CHAR_TYPE {
        NUM, SMALL_LETTER, CAPITAL_LETTER, OTHER_CHAR
    }
}
